package com.ylr.config.framework.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ylr.config.common.constant.ConfigConstant;
import com.ylr.config.common.dto.mapping.MappingDto;
import com.ylr.config.common.vo.mapping.MappingVo;
import com.ylr.config.framework.entity.MappingEntity;
import com.ylr.config.framework.mapper.MappingMapper;
import com.ylr.config.framework.service.MappingRecordService;
import com.ylr.config.framework.service.MappingService;
import com.ylr.base.common.constant.RedisConstant;
import com.ylr.base.common.enums.CommonEnum;
import com.ylr.base.common.enums.ResultEnum;
import com.ylr.base.common.exception.YlrException;
import com.ylr.base.common.po.user.UserPo;
import com.ylr.base.common.utils.DataUtils;
import com.ylr.base.common.utils.RandomNumUtils;
import com.ylr.base.common.utils.ThrottleUtils;
import com.ylr.base.common.utils.UserUtils;
import com.ylr.base.framework.service.AsyncService;
import com.ylr.base.framework.service.SnowflakeService;
import com.ylr.config.common.dto.mapping.SimpleMappingDto;
import com.ylr.redis.framework.service.RedisService;
import com.ylr.redis.framework.service.RedisStringService;
import com.ylr.redis.framework.service.RedissonService;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2023-05-02 18:58:56
 * className: MappingServiceImpl
 * version: 1.0
 * description:
 */
@Service("mappingService")
@AllArgsConstructor
public class MappingServiceImpl extends ServiceImpl<MappingMapper, MappingEntity> implements MappingService {

    private static final Logger log = LoggerFactory.getLogger(MappingServiceImpl.class);

    private final SnowflakeService snowflakeService;

    private final MappingRecordService mappingRecordService;

    private final AsyncService asyncService;

    private final RedisStringService redisStringService;

    private final RedisService redisService;

    private final RedissonService redissonService;

    private final ThrottleUtils throttleUtils;

    @Override
    public void saveMapping(MappingVo mappingVo) {
        // 获取正在保存键值对映射的用户对象
        UserPo userPo = UserUtils.getUser();
        Date date = new Date();

        MappingEntity mapping = new MappingEntity();
        // 属性赋值
        BeanUtils.copyProperties(mappingVo, mapping);
        mapping.setId(null)
                .setStatus(CommonEnum.STATUS_USE.getValue())
                .setDeleteStatus(CommonEnum.DELETE_NO.getValue())
                .setUpdateTime(date)
                .setUpdateUser(StringUtils.EMPTY)
                .setUpdateUserId(CommonEnum.DEFAULT_ID.getLongValue())
                .setCreateTime(date)
                .setCreateUser(userPo.getUsername())
                .setCreateUserId(userPo.getId());

        // 判断key是否存在
        LambdaQueryWrapper<MappingEntity> mappingLambdaQueryWrapper = new LambdaQueryWrapper<>();
        mappingLambdaQueryWrapper.eq(MappingEntity::getMapKey, mappingVo.getMapKey())
                .eq(MappingEntity::getDeleteStatus, CommonEnum.DELETE_NO.getValue());
        // 查询数据
        List<MappingEntity> mappingList = baseMapper.selectList(mappingLambdaQueryWrapper);
        if (!mappingList.isEmpty()) {
            if (mappingList.size() > 1) {
                log.warn("键值对配置 => 相同key个数大于1。size={}", mappingList.size());
            }
            // 取最后一个
            MappingEntity _mapping = mappingList.get(mappingList.size() - 1);
            mapping.setId(_mapping.getId())
                    .setStatus(_mapping.getStatus())
                    .setUpdateUser(userPo.getUsername())
                    .setUpdateUserId(userPo.getId())
                    .setCreateTime(_mapping.getCreateTime())
                    .setCreateUser(_mapping.getCreateUser())
                    .setCreateUserId(_mapping.getCreateUserId());
        }
        if (Objects.isNull(mapping.getId())) {
            // 设置主键
            mapping.setId(snowflakeService.getId());
            // 插入数据
            baseMapper.insert(mapping);
            log.info("键值对映射数据插入完成");
        } else {
            // 更新数据
            baseMapper.updateById(mapping);
            log.info("键值对映射数据更新完成");

            // 取消正在使用的键值对映射数据
            mappingRecordService.deleteUseMapping(mapping.getId());

            // 删除缓存
            Boolean bool = redisService.delete(ConfigConstant.MAPPING_PREFIX + mapping.getMapKey());
            log.info("缓存删除完成。bool={}", bool);
            asyncService.schedule(() -> {
                // 延时双删
                Boolean _bool = redisService.delete(ConfigConstant.MAPPING_PREFIX + mapping.getMapKey());
                log.info("缓存延时双删完成。bool={}", _bool);
            }, 1500, TimeUnit.MILLISECONDS);
        }

        // 数据记录
        asyncService.runAsync(() -> {
            mappingRecordService.saveMappingRecord(mapping.getId(), mapping.getMapName(), mapping.getMapKey(), mapping.getMapValue(), mapping.getSort(), mapping.getStatus(), mapping.getRemark());
        });
    }

    @Override
    public MappingDto getMapping(Long id) {

        LambdaQueryWrapper<MappingEntity> mappingLambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 查询字段
        mappingLambdaQueryWrapper.select(MappingEntity::getMapName, MappingEntity::getMapKey, MappingEntity::getMapValue,
                MappingEntity::getSort, MappingEntity::getStatus, MappingEntity::getRemark);
        // 匹配条件
        mappingLambdaQueryWrapper.eq(MappingEntity::getId, id)
                .eq(MappingEntity::getDeleteStatus, CommonEnum.DELETE_NO.getValue());
        // 查询数据
        List<MappingEntity> mappingList = baseMapper.selectList(mappingLambdaQueryWrapper);
        if (mappingList.isEmpty()) {
            log.info("键值对数据已被删除");
            throw new YlrException(ResultEnum.REFRESH);
        }
        MappingEntity mapping = mappingList.get(0);

        MappingDto mappingDto = new MappingDto();
        mappingDto.setId(id.toString())
                .setMapName(mapping.getMapName())
                .setMapKey(mapping.getMapKey())
                .setMapValue(mapping.getMapValue())
                .setSort(mapping.getSort())
                .setStatus(mapping.getStatus())
                .setRemark(mapping.getRemark());
        return mappingDto;
    }

    @Override
    public SimpleMappingDto getSimpleMapping(String mapKey) {

        String redisKey = ConfigConstant.MAPPING_PREFIX + mapKey;
        // 查询缓存
        SimpleMappingDto cacheSimpleMappingDto = redisStringService.get(redisKey);
        if (Objects.nonNull(cacheSimpleMappingDto)) {
            Runnable runnable = () -> {
                // 尝试刷新键的过期时间
                redisService.tryUpdateKeyExpire(redisKey, 150, RandomNumUtils.random5Minutes(10));
            };
            // 节流执行
            throttleUtils.throttleRun(runnable, "getSimpleMapping" + mapKey, 1, TimeUnit.SECONDS);
            return cacheSimpleMappingDto;
        }
        String lockKey = RedisConstant.LOCK_PREFIX + redisKey;
        Supplier<SimpleMappingDto> supplier = () -> {
            // double check
            SimpleMappingDto simpleMappingDto = redisStringService.get(redisKey);
            if (Objects.nonNull(simpleMappingDto)) {
                return simpleMappingDto;
            }

            LambdaQueryWrapper<MappingEntity> mappingLambdaQueryWrapper = new LambdaQueryWrapper<>();
            // 查询字段
            mappingLambdaQueryWrapper.select(MappingEntity::getMapName, MappingEntity::getMapKey, MappingEntity::getMapValue);
            // 匹配条件
            mappingLambdaQueryWrapper.eq(MappingEntity::getMapKey, mapKey)
                    .eq(MappingEntity::getDeleteStatus, CommonEnum.DELETE_NO.getValue());
            // 查询数据
            List<MappingEntity> mappingList = baseMapper.selectList(mappingLambdaQueryWrapper);
            if (mappingList.isEmpty()) {
                log.info("键值对数据已被删除");
                throw new YlrException(ResultEnum.NOT_FOUND);
            }
            MappingEntity mapping = mappingList.get(0);

            simpleMappingDto = new SimpleMappingDto();
            simpleMappingDto.setMapName(mapping.getMapName())
                    .setMapKey(mapping.getMapKey())
                    .setMapValue(mapping.getMapValue());

            // 缓存五分钟
            redisStringService.set(redisKey, simpleMappingDto, RandomNumUtils.random5Minutes(10), TimeUnit.SECONDS);

            return simpleMappingDto;
        };
        // 加锁运行
        return redissonService.lockRun(lockKey, supplier);
    }

    @Override
    public void updateMapping(MappingVo mappingVo) {

        // 获取正在修改键值对映射的用户对象
        UserPo userPo = UserUtils.getUser();
        Date date = new Date();

        MappingEntity mapping = new MappingEntity();
        // 属性值复制
        BeanUtils.copyProperties(mappingVo, mapping);
        mapping.setUpdateTime(date)
                .setUpdateUser(userPo.getUsername())
                .setUpdateUserId(userPo.getId());
        // 更新数据
        int count = baseMapper.updateById(mapping);
        log.info("键值对映射更新完成。count={}", count);
        if (count == 0) {
            throw new YlrException(ResultEnum.NOT_FOUND);
        }

        // 删除缓存
        Boolean bool = redisService.delete(ConfigConstant.MAPPING_PREFIX + mapping.getMapKey());
        log.info("缓存删除完成。bool={}", bool);
        asyncService.schedule(() -> {
            // 延时双删
            Boolean _bool = redisService.delete(ConfigConstant.MAPPING_PREFIX + mapping.getMapKey());
            log.info("缓存延时双删完成。bool={}", _bool);
        }, 1500, TimeUnit.MILLISECONDS);

        asyncService.runAsync(() -> {
            // 取消正在使用的键值对映射数据
            mappingRecordService.deleteUseMapping(mapping.getId());
            // 数据记录
            mappingRecordService.saveMappingRecord(mapping.getId(), mapping.getMapName(), mapping.getMapKey(), mapping.getMapValue(), mapping.getSort(), mapping.getStatus(), mapping.getRemark());
        });
    }

    @Override
    public void deleteMapping(List<Long> idList) {

        LambdaQueryWrapper<MappingEntity> mappingLambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 查询字段
        mappingLambdaQueryWrapper.select(MappingEntity::getId, MappingEntity::getMapKey);
        // 匹配条件
        mappingLambdaQueryWrapper.in(MappingEntity::getId, idList)
                .eq(MappingEntity::getDeleteStatus, CommonEnum.DELETE_NO.getValue());
        // 查询数据
        List<MappingEntity> mappingList = baseMapper.selectList(mappingLambdaQueryWrapper);
        if (mappingList.isEmpty()) {
            log.info("键值对数据已被删除");
            throw new YlrException(ResultEnum.REFRESH);
        }
        List<Long> _idList = new ArrayList<>(mappingList.size());
        List<String> keyList = new ArrayList<>(mappingList.size());
        StringBuilder sb = new StringBuilder(ConfigConstant.MAPPING_PREFIX);
        int length = ConfigConstant.MAPPING_PREFIX.length();
        for (MappingEntity mapping : mappingList) {
            _idList.add(mapping.getId());
            // 替换字符串
            DataUtils.replaceStringBuilder(sb, length, mapping.getMapKey());
            keyList.add(sb.toString());
        }

        // 获取正在删除键值对映射的用户对象
        UserPo userPo = UserUtils.getUser();
        Date date = new Date();

        LambdaUpdateWrapper<MappingEntity> mappingLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        // 设置字段值
        mappingLambdaUpdateWrapper.set(MappingEntity::getDeleteStatus, CommonEnum.DELETE_YES.getValue())
                .set(MappingEntity::getUpdateTime, date)
                .set(MappingEntity::getUpdateUser, userPo.getUsername())
                .set(MappingEntity::getUpdateUserId, userPo.getId());
        // 匹配条件
        mappingLambdaUpdateWrapper.in(MappingEntity::getId, _idList)
                .eq(MappingEntity::getDeleteStatus, CommonEnum.DELETE_NO.getValue());
        // 更新数据
        int count = baseMapper.update(null, mappingLambdaUpdateWrapper);
        log.info("键值对映射删除操作完成。count={}", count);
        if (count == 0) {
            throw new YlrException(ResultEnum.REFRESH);
        }

        // 删除缓存
        Long num = redisService.delete(keyList);
        log.info("缓存删除完成。num={}", num);
        asyncService.schedule(() -> {
            // 延时双删
            Long _num = redisService.delete(keyList);
            log.info("缓存延时双删完成。num={}", _num);
        }, 1500, TimeUnit.MILLISECONDS);
    }

}